/**
 * Code Syntax Highlighter.
 * Version 1.5.1
 * Copyright (C) 2004-2007 Alex Gorbatchev.
 * http://www.dreamprojections.com/syntaxhighlighter/
 * 
 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General 
 * Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) 
 * any later version.
 *
 * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied 
 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to 
 * the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
 */

//
// create namespaces
//
var dp = {
  sh :
  {
    RegexLib: {},
    Brushes  : {},
    Version : '1.5.1'
  }
};

// make an alias
dp.SyntaxHighlighter = dp.sh;

//
// Common reusable regular expressions
//
dp.sh.RegexLib = {
  MultiLineCComments : new RegExp('/\\*[\\s\\S]*?\\*/', 'gm'),
  SingleLineCComments : new RegExp('//.*$', 'gm'),
  SingleLinePerlComments : new RegExp('#.*$', 'gm'),
  DoubleQuotedString : new RegExp('"(?:\\.|(\\\\\\")|[^\\""\\n])*"', 'g'),
  SingleQuotedString : new RegExp("'(?:\\.|(\\\\\\')|[^\\''\\n])*'", 'g')
};

//
// Match object
//
dp.sh.Match = function(value, index, css)
{
  this.value = value;
  this.index = index;
  this.length = value.length;
  this.css = css;
}

//
// Highlighter object
//
dp.sh.Highlighter = function() {
};

// static callback for the match sorting
dp.sh.Highlighter.SortCallback = function(m1, m2)
{
  // sort matches by index first
  if (m1.index < m2.index)
    return -1;
  else if (m1.index > m2.index)
    return 1;
  else
  {
    // if index is the same, sort by length
    if (m1.length < m2.length)
      return -1;
    else if (m1.length > m2.length)
      return 1;
  }
  return 0;
}

dp.sh.Highlighter.prototype.CreateElement = function(name)
{
  return document.createElement(name);
}

// gets a list of all matches for a given regular expression
dp.sh.Highlighter.prototype.GetMatches = function(matches, code, regex, css)
{
  var index = 0;
  var match = null;

  while ((match = regex.exec(code)) != null)
    matches[matches.length] = new dp.sh.Match(match[0], match.index, css);
}

dp.sh.Highlighter.prototype.AddBit = function(div, str, css)
{
  if (str == null || str.length == 0)
    return;

  var span = this.CreateElement('SPAN');

  //	str = str.replace(/&/g, '&amp;');
  str = str.replace(/ /g, '&nbsp;');
  str = str.replace(/</g, '&lt;');
  //	str = str.replace(/&lt;/g, '<');
  //	str = str.replace(/>/g, '&gt;');
  str = str.replace(/\n/gm, '&nbsp;<br>');

  // when adding a piece of code, check to see if it has line breaks in it
  // and if it does, wrap individual line breaks with span tags
  if (css != null)
  {
    if ((/br/gi).test(str))
    {
      var lines = str.split('&nbsp;<br>');

      for (var i = 0; i < lines.length; i++)
      {
        span = this.CreateElement('SPAN');
        span.className = css;
        span.innerHTML = lines[i];

        div.appendChild(span);

        // don't add a <BR> for the last line
        if (i + 1 < lines.length)
          div.appendChild(this.CreateElement('BR'));
      }
    }
    else
    {
      span.className = css;
      span.innerHTML = str;
      div.appendChild(span);
    }
  }
  else
  {
    span.innerHTML = str;
    div.appendChild(span);
  }
}

// checks if one match is inside any other match
dp.sh.Highlighter.prototype.IsInside = function(matches, match)
{
  if (match == null || match.length == 0)
    return false;

  for (var i = 0; i < matches.length; i++)
  {
    var c = matches[i];

    if (c == null)
      continue;

    if ((match.index > c.index) && (match.index < c.index + c.length))
      return true;
  }

  return false;
}

dp.sh.Highlighter.prototype.ProcessRegexList = function(code)
{
  var matches = new Array();
  for (var i = 0; i < this.regexList.length; i++)
    this.GetMatches(matches, code, this.regexList[i].regex, this.regexList[i].css);
  return matches;
}

dp.sh.Highlighter.prototype.ProcessSmartTabs = function(code)
{
  var lines = code.split('\n');
  var result = '';
  var tabSize = 4;
  var tab = '\t';

  // This function inserts specified amount of spaces in the string
  // where a tab is while removing that given tab.
  function InsertSpaces(line, pos, count)
  {
    var left = line.substr(0, pos);
    var right = line.substr(pos + 1, line.length);
    // pos + 1 will get rid of the tab
    var spaces = '';

    for (var i = 0; i < count; i++)
      spaces += ' ';

    return left + spaces + right;
  }

  // This function process one line for 'smart tabs'
  function ProcessLine(line, tabSize)
  {
    if (line.indexOf(tab) == -1)
      return line;

    var pos = 0;

    while ((pos = line.indexOf(tab)) != -1)
    {
      // This is pretty much all there is to the 'smart tabs' logic.
      // Based on the position within the line and size of a tab,
      // calculate the amount of spaces we need to insert.
      var spaces = tabSize - pos % tabSize;

      line = InsertSpaces(line, pos, spaces);
    }

    return line;
  }

  // Go through all the lines and do the 'smart tabs' magic.
  for (var i = 0; i < lines.length; i++)
    result += ProcessLine(lines[i], tabSize) + '\n';

  return result;
}

dp.sh.Highlighter.prototype.Highlight = function(code, tabsToSpaces)
{
  function Trim(str)
  {
    return str.replace(/^\s*(.*?)[\s\n]*$/g, '$1');
  }

  function Chop(str)
  {
    return str.replace(/\n*$/, '').replace(/^\n*/, '');
  }

  function Unindent(str)
  {
    var lines = str.split('\n');
    var indents = new Array();
    var regex = new RegExp('^\\s*', 'g');
    var min = 1000;

    // go through every line and check for common number of indents
    for (var i = 0; i < lines.length && min > 0; i++)
    {
      if (Trim(lines[i]).length == 0)
        continue;

      var matches = regex.exec(lines[i]);

      if (matches != null && matches.length > 0)
        min = Math.min(matches[0].length, min);
    }

    // trim minimum common number of white space from the begining of every line
    if (min > 0)
      for (var i = 0; i < lines.length; i++)
        lines[i] = lines[i].substr(min);

    return lines.join('\n');
  }

  // This function returns a portions of the string from pos1 to pos2 inclusive
  function Copy(string, pos1, pos2)
  {
    return string.substr(pos1, pos2 - pos1);
  }

  if (code == null)
    code = '';

  // parse code
  var codeContainer = this.CreateElement('DIV');

  // uses lines
  code = Chop(Unindent(code));

  // replace tabs with spaces
  // uses lines
  if (tabsToSpaces)
    code = this.ProcessSmartTabs(code);

  var matches = this.ProcessRegexList(code);

  // if no matches found, add entire code as plain text
  if (matches.length == 0)
  {
    this.AddBit(codeContainer, code, null);
    return codeContainer.innerHTML;
  }

  // sort the matches
  matches = matches.sort(dp.sh.Highlighter.SortCallback);

  // The following loop checks to see if any of the matches are inside
  // of other matches. This process would get rid of highligted strings
  // inside comments, keywords inside strings and so on.
  for (var i = 0; i < matches.length; i++)
    if (this.IsInside(matches, matches[i]))
      matches[i] = null;

    // Finally, go through the final list of matches and pull them all
    // together adding everything in between that isn't a match.
  var pos = 0;
  for (var i = 0; i < matches.length; i++)
  {
    var match = matches[i];

    if (match == null || match.length == 0)
      continue;

    this.AddBit(codeContainer, Copy(code, pos, match.index), null);
    this.AddBit(codeContainer, match.value, match.css);

    pos = match.index + match.length;
  }
  // add end
  this.AddBit(codeContainer, code.substr(pos), null);

  return codeContainer.innerHTML;
}

dp.sh.Highlighter.prototype.GetKeywords = function(str)
{
  return '\\b' + str.replace(/ /g, '\\b|\\b') + '\\b';
}